package com.java80.littlegame.service.gateway.session;

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.java80.littlegame.common.utils.MD5Security;

import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;

public class ChannelManageCenter {

	public static final int CHANNEL_CONNECTED = 1;

	public static final int CHANNEL_DISCONNECTED = 2;

	final transient static Logger log = LoggerFactory.getLogger(ChannelManageCenter.class);

	private static ChannelManageCenter cmcIns = new ChannelManageCenter();

	/** channel and connectSession mapping **/
	private DualHashBidiMap connectSessionMap;

	/** channel and String mapping **/
	private DualHashBidiMap tempCache;

	private ChannelGroup cg;

	private ConcurrentHashMap<Long, ConnectSession> sessionPool;

	private ScheduledThreadPoolExecutor schedule;

	public static final AttributeKey<ChannelAttach> akey = AttributeKey.valueOf("attach");

	private ChannelManageCenter() {

		connectSessionMap = new DualHashBidiMap();

		tempCache = new DualHashBidiMap();

		cg = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

		sessionPool = new ConcurrentHashMap<Long, ConnectSession>();

		schedule = new ScheduledThreadPoolExecutor(1);

		// checkIllegalChannel();
	}

	/***
	 * get channel manager center instance
	 * 
	 * @return
	 */
	public static ChannelManageCenter getInstance() {
		return cmcIns;
	}

	/****
	 * 生成sesseion ID
	 * 
	 * @param ch
	 * @return
	 */
	private String genSesseionId(Channel ch) {
		try {
			return MD5Security.md5hex32(ch.hashCode() + "" + ch.remoteAddress()).toLowerCase();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private void checkIllegalChannel() {
		schedule.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				if (cmcIns.tempCache.size() == 0) {
					return;
				}
				synchronized (cmcIns.tempCache) {
					try {
						Iterator<Object> ite = cmcIns.tempCache.mapIterator();
						while (ite.hasNext()) {
							Object obj = ite.next();
							Channel ch = (Channel) obj;
							Attribute<ChannelAttach> attr = ch.attr(akey);
							Object atta = attr.get();
							if (atta != null && (atta instanceof ChannelAttach)) {
								long time = System.currentTimeMillis();
								ChannelAttach att = (ChannelAttach) atta;
								if (time - att.getActivityTime() > 5 * 1000) {
									log.info("非法链接在临时链接池持续超过5S:" + att.getChannel().remoteAddress() + "，断开连接!");
									ch.close().sync();
								}
							}
						}
					} catch (Exception e) {
						log.error("", e);
					}
				}
			}
		}, 0, 1, TimeUnit.SECONDS);
	}

	/***
	 * 添加信道到临时缓存
	 * 
	 * @param ch
	 * @param userId
	 */
	public String addChannel(Channel ch) {
		String name = genSesseionId(ch);
		// System.out.println("addChannel = ch "+ch.remoteAddress()+" ,"+name);
		Attribute<ChannelAttach> att = ch.attr(akey);
		if (att != null && att.get() != null) {
			att.get().setActivityTime(System.currentTimeMillis());
		} else {
			ChannelAttach ca = new ChannelAttach(ch, System.currentTimeMillis());
			att.setIfAbsent(ca);
		}
		tempCache.put(ch, name);
		return name;
	}

	/***
	 * get connectSession by channel
	 * 
	 * @param ch
	 * @return
	 */
	public ConnectSession getSession(Channel ch) {
		if (cmcIns.connectSessionMap.containsKey(ch)) {
			ConnectSession session = (ConnectSession) cmcIns.connectSessionMap.get(ch);
			return session;
		}
		return null;
	}

	/***
	 * get connectSession by channel
	 * 
	 * @param ch
	 * @return
	 */
	public String getTempID(Channel ch) {
		if (cmcIns.tempCache.containsKey(ch)) {
			String session = (String) cmcIns.tempCache.get(ch);
			return session;
		}
		return null;
	}

	/***
	 * remove the cached channel
	 * 
	 * @param ch
	 */
	public void removeTempSession(Channel ch) {
		if (cmcIns.tempCache.containsKey(ch)) {
			// System.out.println("removeTempSession - "+ch.remoteAddress());
			cmcIns.tempCache.remove(ch);
		}
	}

	public void removeSessionPool(Long userId) {
		if (cmcIns.sessionPool.containsKey(userId)) {
			// System.out.println("removeTempSession - "+ch.remoteAddress());
			cmcIns.sessionPool.remove(userId);
		}
	}

	public boolean bind(String channelUid, long userId) {
		log.info("给userId:" + userId + "绑定session");
		String value = channelUid;
		if (cmcIns.tempCache.containsValue(value)) {
			Channel ch = (Channel) cmcIns.tempCache.removeValue(value);

			if (cmcIns.sessionPool.containsKey(userId)) {
				// 改玩家session 已存在。挤用户下线
				ConnectSession session = cmcIns.sessionPool.get(userId);
				Channel last = session.getChannel();
				if (last != null) {
					// TODO Tips 别处登陆 （参见 SystemTipMsg）
					/*
					 * String str = "{\"ti\":4}"; last.writeAndFlush(new
					 * RawMessage(6, 60009, str).serialized());
					 */
					session.setChannel(ch);
					session.setSesseionId(channelUid);
					cg.remove(last);
					cg.add(ch);
					cmcIns.connectSessionMap.remove(last);
					cmcIns.connectSessionMap.put(ch, session);
					// System.out.println("new channel binding " +
					// ch.remoteAddress());

					last.disconnect();
					// System.out.println("last channel disconnect " +
					// last.remoteAddress());
				}
				return true;

			} else {

				cg.add(ch);

				ConnectSession session = new ConnectSession(channelUid, userId).setChannel(ch);

				cmcIns.connectSessionMap.put(ch, session);

				cmcIns.sessionPool.put(userId, session);

				log.info("userId:" + userId + "绑定session成功");

				return true;
			}
		} else {
			return false;
		}
	}

	public void clearSession(long userId) {
		ConnectSession session = cmcIns.sessionPool.remove(userId);
		cmcIns.connectSessionMap.removeValue(session);
	}

	public Channel getChannel(String sessionId) {
		if (cmcIns.tempCache.containsValue(sessionId)) {
			Object Object = cmcIns.tempCache.getKey(sessionId);
			return (Channel) Object;
		} else {

		}
		return null;
	}

	public ConnectSession getSession(long uid) {
		return cmcIns.sessionPool.get(uid);
	}

	public ChannelGroup getAllChannels() {
		return cg;

	}
}
